//--------------------------------------------------------------------
#include <avr/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "dkbasic.h"
#include "dkvariables.h"
#include "dkstring.h"
#include "dkprogram.h"

//--------------------------------------------------------------------

// dkvariables is a linked list of struct _dkvariables
struct _dkvariable *dkvariables;


//--------------------------------------------------------------------
// does not make a copy of data - just assigns this ptr = passed ptr
void dkfillarray( char *name, uint16_t type, void *data, int numdims, uint16_t *dims, int len )
{
	int i;
	int dimsize;
	struct _dkvariable *setvar;
	struct _dkvariable *listptr;

	
	setvar = dkgetvariable( name, type );		// get pointer to struct _dkvariable
	if( setvar )
        dkremovearray( name, type );

	                                    // variable doesn't exist yet, create it with these dimensions
	setvar = (struct _dkvariable *)malloc( sizeof(struct _dkvariable) );
	if( !setvar )
	{
		dkstatus=DKSTATUS_OUTOFMEMORY;
		return;
	}
	
	setvar->next = NULL;
	
	setvar->name = (char *)malloc( strlen(name) );
	if(!setvar->name)		
	{
		dkstatus=DKSTATUS_OUTOFMEMORY;
		return;
	}
	strcpy( setvar->name, name );
	
	setvar->e.type = type;
	setvar->e.numdimensions = numdims;
	setvar->e.dimensions = (uint16_t *)malloc(sizeof(uint16_t)*numdims);
	if(!setvar->e.dimensions)
	{
		dkstatus=DKSTATUS_OUTOFMEMORY;
		return;
	}
	dimsize=1;
	for( i=0; i<numdims; i++ )
	{
		setvar->e.dimensions[i] = dims[i];
		dimsize *= (dims[i]+1);				// allocate an extra space in each dim for [0] element
	}
		
	listptr = dkvariables;
	while( listptr )					// loop to point listptr to last item in list
	{
		if( !listptr->next )
			break;
		listptr = listptr->next;		
	}

	if( listptr )
		listptr->next = setvar;			// link newvar to end of list
	else
		dkvariables = setvar;			// newvar is first item in list			



    setvar->e.data = data;
    

	
	return;
}


//--------------------------------------------------------------------
void dksetinarray( char *name, uint16_t type, void *data, int numdims, uint16_t *dims )
{
	int i,j;
	int ofs;
	int dimsize;
	struct _dkvariable *setvar;
	struct _dkvariable *listptr;

	
	setvar = dkgetvariable( name, type );		// get pointer to struct _dkvariable
	if( !setvar )
	{											// variable doesn't exist yet, create it with these dimensions
		setvar = (struct _dkvariable *)malloc( sizeof(struct _dkvariable) );
		if( !setvar )
		{
			dkstatus=DKSTATUS_OUTOFMEMORY;
			return;
		}
		
		setvar->next = NULL;
		
		setvar->name = (char *)malloc( strlen(name) );
		if(!setvar->name)		
		{
			dkstatus=DKSTATUS_OUTOFMEMORY;
			return;
		}
		strcpy( setvar->name, name );
		
		setvar->e.type = type;
		setvar->e.numdimensions = numdims;
	    setvar->e.dimensions = (uint16_t *)malloc(sizeof(uint16_t) * numdims);

		dimsize=1;
    	for( i=0; i<numdims; i++ )
    	{
		    setvar->e.dimensions[i] = dims[i];
		    dimsize *= (dims[i]+1);				// allocate an extra space in each dim for [0] element
	    }
	    

//        setvar->e.data = data;

	if( setvar->e.type & DKVAR_STRING )
	{
		setvar->e.data = (char **)malloc( sizeof(char *) * dimsize );
		if( !setvar->e.data )
			return;					// malloc error

		for( i=0; i<dimsize; i++ )
		{
			((char **)setvar->e.data)[i] = (char *)malloc( 1 );				
			((char **)setvar->e.data)[i][0] = '\0';			// each entry in array is a one-byte empty string
		}
	}
	else if( setvar->e.type & DKVAR_FLOATSINGLE )
	{
		setvar->e.data = (double *)malloc( sizeof(double) * dimsize );
		if( !setvar->e.data )
			return;					// malloc error

		for( i=0; i<dimsize; i++ )
			((double *)setvar->e.data)[i] = 0;
	}
	else if( setvar->e.type & DKVAR_FLOATDOUBLE )
	{
		setvar->e.data = (double *)malloc( sizeof(double) * dimsize );
		if( !setvar->e.data )
			return;					// malloc error

		for( i=0; i<dimsize; i++ )
			((double *)setvar->e.data)[i] = 0;
	}
	else if( setvar->e.type & DKVAR_INTEGER )
	{
		setvar->e.data = (uint16_t *)malloc( sizeof(uint16_t) * dimsize );
		if( !setvar->e.data )
			return;					// malloc error

		for( i=0; i<dimsize; i++ )
			((uint16_t *)setvar->e.data)[i] = 0;
	}
	else if( setvar->e.type & DKVAR_BYTE )
	{
		setvar->e.data = (uint8_t *)malloc( sizeof(uint8_t) * dimsize );
		if( !setvar->e.data )
			return;					// malloc error

		for( i=0; i<dimsize; i++ )
			((uint8_t *)setvar->e.data)[i] = 0;
	}
	else
		setvar->e.data = NULL;


			
		listptr = dkvariables;
		while( listptr )					// loop to point listptr to last item in list
		{
			if( !listptr->next )
				break;
			listptr = listptr->next;		
		}

		if( listptr )
			listptr->next = setvar;			// link newvar to end of list
		else
			dkvariables = setvar;			// newvar is first item in list			
	}


	if( numdims > setvar->e.numdimensions )
	{
		dkstatus=DKSTATUS_SUBSCRIPTOUTOFRANGE;
		return;
	}

	ofs = 0;									// start at beginning of data
	for( i=0; i<numdims-1; i++ )				// loop through 
	{
		dimsize = 1;
		for( j=setvar->e.numdimensions; j>i; j-- )
			dimsize *= setvar->e.dimensions[j-1];	// make dimsize = item size in this dimension
		
		if( dims[i] > setvar->e.dimensions[i] )
		{
			dkstatus=DKSTATUS_SUBSCRIPTOUTOFRANGE;
			return;
		}
			
		ofs += dimsize * dims[i];
	}
	ofs += dims[i];
	if( dims[i] > setvar->e.dimensions[i] )
	{
		dkstatus=DKSTATUS_SUBSCRIPTOUTOFRANGE;
		return;
	}


	if( setvar->e.type & DKVAR_STRING )
	{
		if( ((char **)setvar->e.data)[ofs] )
			free( ((char **)setvar->e.data)[ofs] );	// free memory used by previous value
		((char **)setvar->e.data)[ofs] = (char *)malloc( strlen( ((char **)data)[0] )+1 );
		if( !((char **)setvar->e.data)[ofs] )
			return;					// malloc error
		strcpy( ((char **)setvar->e.data)[ofs], ((char **)data)[0] );
	}
	else if( setvar->e.type & DKVAR_FLOATSINGLE )
	{
		((double *)setvar->e.data)[ofs] = ((double *)data)[0];
	}
	else if( setvar->e.type & DKVAR_FLOATDOUBLE )
	{
		((double *)setvar->e.data)[ofs] = ((double *)data)[0];
	}
	else if( setvar->e.type & DKVAR_INTEGER )
	{
		((int16_t *)setvar->e.data)[ofs] = ((int16_t *)data)[0];
	}
	else if( setvar->e.type & DKVAR_BYTE )
	{
		((int8_t *)setvar->e.data)[ofs] = ((int8_t *)data)[0];
	}



	
	return;
}

//--------------------------------------------------------------------
// note: returned e->data points into same data as the array in dkvariables
dkexpression *dkgetexpfromarray( char *name, uint16_t type, int numdims, uint16_t *dims )
{
	int i;
	int j;
	struct _dkvariable *var;
	dkexpression *exp=NULL;
	int ofs;									// ofs = index into var->data of requested item of array
	int dimsize;								// dimsize = size of each item in array at current dimension
	
	
	var = dkgetvariable( name, type );			// get pointer to struct _dkvariable

	if( !var )
		return NULL;

	if( numdims > var->e.numdimensions )
	{
		dkstatus=DKSTATUS_SUBSCRIPTOUTOFRANGE;
		return NULL;
	}
	
	
	ofs = 0;									// start at beginning of data
	for( i=0; i<numdims-1; i++ )					// loop through 
	{
		dimsize = 1;
		for( j=var->e.numdimensions; j>i; j-- )
			dimsize *= var->e.dimensions[j-1];	// make dimsize = item size in this dimension

		if( dims[i] > var->e.dimensions[i] )
		{
			dkstatus=DKSTATUS_SUBSCRIPTOUTOFRANGE;
			return NULL;
		}
		
		ofs += dimsize * dims[i];
	}
	if( dims[i] > var->e.dimensions[i] )
	{
		dkstatus=DKSTATUS_SUBSCRIPTOUTOFRANGE;
		return NULL;
	}
	ofs += dims[i];
	

	exp = dknewexp();
	if( !exp )
		return NULL;


	exp->numdimensions=0;
	exp->dimensions=NULL;
	exp->type = var->e.type & (~DKVAR_ARRAY);
	if( var->e.type & DKVAR_STRING )
		exp->data = &((char *)var->e.data)[ofs];
	else if( var->e.type & DKVAR_INTEGER )
		exp->data = &((uint16_t *)var->e.data)[ofs];
	else if( var->e.type & DKVAR_FLOATSINGLE )
		exp->data = &((double *)var->e.data)[ofs];
	else if( var->e.type & DKVAR_FLOATDOUBLE )
		exp->data = &((double *)var->e.data)[ofs];
	else if( var->e.type & DKVAR_BYTE )
		exp->data = &((uint8_t *)var->e.data)[ofs];


	return exp;
}
//--------------------------------------------------------------------

struct _dkvariable *dkgetvariable( char *name, uint16_t type )
{
	struct _dkvariable *listptr = dkvariables;


	strlwr( name );						// make sure name is all lowercase
	if( !type )
		type = DKVAR_DEFAULT;			// if no type was specified, look for default type

	while( listptr )
	{
		if( (!strcmp( name, listptr->name )) && (listptr->e.type==type) )
		{								// matched item in list
			return listptr;
		}
		listptr = listptr->next;		// point listptr to next item in list
	}


	return NULL;						// variable not found in list
}

//--------------------------------------------------------------------
void dkremovevariable( char *name, uint16_t type )
{
	struct _dkvariable *listptr = dkvariables;
	struct _dkvariable *prevptr = NULL;


	strlwr( name );						// make sure name is all lowercase
	if( !type )
		type = DKVAR_DEFAULT;			// if no type was specified, look for default type

	while( listptr )
	{
		if( (!strcmp( name, listptr->name )) && (listptr->e.type==type) )
			break;						// matched item in list
		prevptr = listptr;
		listptr = listptr->next;		// point listptr to next item in list		
	}

	if( listptr )
	{									// matched variable to remove
		if( prevptr )
			prevptr->next = listptr->next;	// remove this item from the linked list
		else
			dkvariables = listptr->next;

		if( listptr->e.type == DKVAR_STRING )
			if( ((char **)listptr->e.data)[0] )
				free( ((char **)listptr->e.data)[0] );	// variable is string, free space used by the string
		free( listptr->e.data );				
				
		free( (char *)listptr->name );	// free space used by variable name
		free( listptr);					// free space used by variable item in the linked list
	}


	return;								// variable not found in list
}

//--------------------------------------------------------------------

void dksetvariable( char *name, uint16_t type, void *data )
{
	struct _dkvariable *setvar;


//	if( value == 0 || ( type==DKVAR_STRING && !strlen((char *)value) ) )
	if( type==DKVAR_STRING && !strlen(((char **)data)[0]) )
	{
		dkremovevariable( name, type );
		return;
	}

	setvar = dkgetvariable( name, type );
	if( setvar )
	{									// variable exists in list
		if( setvar->e.type == DKVAR_STRING )
		{
			if( ((char **)setvar->e.data)[0] )
				free( ((char **)setvar->e.data)[0] );	// free memory used by previous value
			((char **)setvar->e.data)[0] = (char *)malloc( strlen( ((char **)data)[0] )+1 );
			if( !((char **)setvar->e.data)[0] )
				return;					// malloc error
			strcpy( ((char **)setvar->e.data)[0], ((char **)data)[0] );
		}
		else if( setvar->e.type == DKVAR_FLOATSINGLE )
		{
			((double *)setvar->e.data)[0] = ((double *)data)[0];
		}
		else if( setvar->e.type == DKVAR_FLOATDOUBLE )
		{
			((double *)setvar->e.data)[0] = ((double *)data)[0];
		}
		else if( setvar->e.type == DKVAR_INTEGER )
		{
//			if( setvar->e.data )
//				free( setvar->e.data );		// free memory used by previous value
//			setvar->e.data = (int16_t *)malloc( sizeof(int16_t) );
//			if( !((int16_t *)setvar->e.data) )
//				return;					// malloc error
			((int16_t *)setvar->e.data)[0] = ((int16_t *)data)[0];
		}
		else if( setvar->e.type == DKVAR_BYTE )
		{
			((uint8_t *)setvar->e.data)[0] = ((uint8_t *)data)[0];
		}

		return;
	}


	dkaddvariable( name, type, data );


	return;
}

//--------------------------------------------------------------------

struct _dkvariable *dkaddvariable( char *name, uint16_t type, void *data )
{
	struct _dkvariable *newvar;
	struct _dkvariable *listptr;


	newvar = (struct _dkvariable *)malloc( sizeof(struct _dkvariable) );
	if( !newvar )
		return NULL;					// memory allocation error

	newvar->next = NULL;

	newvar->name = (char *)malloc( strlen(name)+1 );
	if( !newvar->name )
	{
		free( newvar );
		return NULL;					// memory allocation error
	}
	strcpy( newvar->name, name );

	if( !type )
		type = DKVAR_DEFAULT;			// if no type was specified, look for default type

	newvar->e.type = type;

	if( newvar->e.type == DKVAR_STRING )
	{
		newvar->e.data = (char **)malloc( sizeof(char *) );
		((char **)newvar->e.data)[0] = (char *)malloc( strlen( ((char **)data)[0] )+1 );

		if( !((char **)newvar->e.data)[0] )
		{
			free( newvar->name );
			free( newvar );
			return NULL;				// malloc error
		}
		strcpy( ((char **)newvar->e.data)[0], ((char **)data)[0] );
	}
	else if( newvar->e.type == DKVAR_FLOATSINGLE )
	{
		newvar->e.data = (double *)malloc( sizeof(double) );
		if( !((double *)newvar->e.data) )
		{
			free( newvar->name );
			free( newvar );
			return NULL;				// malloc error
		}

		((double *)newvar->e.data)[0] = ((double *)data)[0];
	}
	else if( newvar->e.type == DKVAR_FLOATDOUBLE )
	{
		newvar->e.data = (double *)malloc( sizeof(double) );
		if( !((double *)newvar->e.data) )
		{
			free( newvar->name );
			free( newvar );
			return NULL;				// malloc error
		}

		((double *)newvar->e.data)[0] = ((double *)data)[0];
	}
	else if( newvar->e.type == DKVAR_INTEGER )
	{
		newvar->e.data = (int16_t *)malloc( sizeof(int16_t) );
		if( !((int16_t *)newvar->e.data) )
		{
			free( newvar->name );
			free( newvar );
			return NULL;				// malloc error
		}

		((int16_t *)newvar->e.data)[0] = ((int16_t *)data)[0];
	}
	else if( newvar->e.type == DKVAR_BYTE )
	{
		newvar->e.data = (int8_t *)malloc( sizeof(int8_t) );
		if( !((int8_t *)newvar->e.data) )
		{
			free( newvar->name );
			free( newvar );
			return NULL;				// malloc error
		}

		((int8_t *)newvar->e.data)[0] = ((int8_t *)data)[0];
	}


	strlwr( name );						// make sure name is all lowercase


	listptr = dkvariables;
	while( listptr )					// loop to point listptr to last item in list
	{
		if( !listptr->next )
			break;
		listptr = listptr->next;		
	}

	if( listptr )
		listptr->next = newvar;			// link newvar to end of list
	else
		dkvariables = newvar;			// newvar is first item in list


	return newvar;
}

//--------------------------------------------------------------------


void dkremovearray( char *name, uint16_t type )
{
	struct _dkvariable *listptr = dkvariables;
	struct _dkvariable *prevptr = NULL;
    int numdims;
    int dimsize;
    int i;
    

	strlwr( name );						// make sure name is all lowercase
	if( !type )
		type = DKVAR_DEFAULT;			// if no type was specified, look for default type

	while( listptr )
	{
		if( (!strcmp( name, listptr->name )) && (listptr->e.type==type) )
			break;						// matched item in list
		prevptr = listptr;
		listptr = listptr->next;		// point listptr to next item in list		
	}

	if( listptr )
	{									// matched variable to remove
		if( prevptr )
			prevptr->next = listptr->next;	// remove this item from the linked list
		else
			dkvariables = listptr->next;


        numdims = listptr->e.numdimensions;
//		setvar->e.dimensions = (uint16_t *)malloc(sizeof(uint16_t)*numdims);
		dimsize=1;
		for( i=0; i<numdims; i++ )
			dimsize *= (listptr->e.dimensions[i]+1);	// allocate an extra space in each dim for [0] element

		if( listptr->e.type & DKVAR_STRING )
		{
			for( i=0; i<dimsize; i++ )
			{
//    	        if( listptr->e.data[i] )
		            free( ((char **)listptr->e.data)[i] );
			}
		}


        free( listptr->e.dimensions );
		free( listptr->e.data );				

		free( (char *)listptr->name );	// free space used by variable name
		free( listptr);					// free space used by variable item in the linked list
	}


	return;								// variable not found in list
}


//--------------------------------------------------------------------



